#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import os

import time
from functools import wraps

from common.common import get_host_sn
from common.common_models import ActionResult, LogDetail, SubJobDetails
from common.const import DBLogLevel, ExecuteResultEnum, SubJobStatusEnum
from exchange import log
from exchange.commons.common import output_result_file, output_execution_result_ex, traverse_folder, \
    report_job_details_by_rpc
from exchange.commons.const import ExchangeReportDBLabel, ExchangeJobInfo, RepositoryDataTypeEnum, \
    ExchangeWindowsPath
from exchange.commons.exchange_exception import ExchangeErrCodeException


def log_start_end():
    """
    功能描述： 函数开始-结束添加日志打印
    """

    def decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):
            log.info(f"Enter {f.__name__}")
            ret = f(*args, **kwargs)
            log.info(f"Exit with [{ret}], {f.__name__}")
            return ret

        return inner

    return decorator


def out_result_with_job_info():
    """
    功能描述： 根据被注解方法返回布尔值，Agent stmp 目录下写ActionResult结构体， Agent读取并上报
    """

    def decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):
            if len(args) < 2:
                raise Exception("Param err")
            job_info = args[1]
            ret = False
            body_err = None
            body_err_params = None
            try:
                ret = f(*args, **kwargs)
            except ExchangeErrCodeException as err:
                body_err = err.get_log_detail().log_detail
                body_err_params = err.get_log_detail().log_detail_param
                log.error(f"{f.__name__} raise ErrCodeException, " \
                          f"body_err: {body_err}, body_err_params: {body_err_params}, {job_info.log_format()}.")
                raise err
            except Exception as err:
                log.error(f"{f.__name__} run err: {err}, {job_info.log_format()}.")
                raise err
            finally:
                ret_code = ExecuteResultEnum.SUCCESS if ret else ExecuteResultEnum.INTERNAL_ERROR
                output_result_file(
                    job_info.pid,
                    ActionResult(code=ret_code, bodyErr=body_err, bodyErrParams=body_err_params).dict(by_alias=True))
                log.info(f"{f.__name__} write result suc, {job_info.log_format()}.")

            return ret

        return inner

    return decorator


def progress_notify_with_job_info(suc_lab=None, fail_lab=None):
    """
    功能描述： 根据被注解方法返回布尔值，cache仓路径写SubJobDetails结构体，progress方法会读取并上报
    suc_lab: 成功任务信息提示label
    fail_lab： 失败任务提示信息label
    """

    def decorator(f):
        @wraps(f)
        def inner(*args, **kwargs):
            job_info = args[1]
            ret = False
            log_detail = None
            try:
                ret = f(*args, **kwargs)
            except ExchangeErrCodeException as err:
                # 已识别报错，抛异常处构造log_detail
                log.error(f"{f.__name__} raise exception: {err.get_log_detail()}, {job_info.log_format()}.")
                log_detail = [err.get_log_detail().dict(by_alias=True)]
                raise err
            except Exception as err:
                # 未识别报错，finally构造log_detail
                log.error(f"{f.__name__} run err: {err}, {job_info.log_format()}.")
                raise err
            finally:
                # 填充任务成功log_detail
                if not log_detail and fail_lab and not ret:
                    log_detail = [
                        LogDetail(logInfo=fail_lab, logTimestamp=int(time.time()),
                                  logLevel=DBLogLevel.ERROR).dict(by_alias=True)
                    ]
                # 填充任务失败log_detail
                if not log_detail and suc_lab and ret:
                    log_detail = [
                        LogDetail(logInfo=suc_lab, logTimestamp=int(time.time()),
                                  logLevel=DBLogLevel.INFO).dict(by_alias=True)
                    ]
                # 填充label参数
                if log_detail:
                    # 根据label名称来填充不同的log_info_param
                    fill_log_info_param(log_detail, job_info)

                # 未知Exchange统计数据量方法, 已备份量、速度暂定0
                data_path = ""
                for rep in job_info.data_reps:
                    if rep.get("repositoryType") == RepositoryDataTypeEnum.DATA_REPOSITORY:
                        data_path = ExchangeWindowsPath.UNC + rep.get("path")[0]
                        break
                total_size = traverse_folder(data_path)
                sub_job_detail = SubJobDetails(
                    taskId=job_info.job_id,
                    subTaskId=job_info.sub_job_id,
                    progress=100 if ret else 0,
                    dataSize=int(total_size/1024),
                    logDetail=log_detail,
                    taskStatus=SubJobStatusEnum.COMPLETED if ret else SubJobStatusEnum.FAILED)
                write_progress_to_file(sub_job_detail, job_info)
                log.info(f"{f.__name__} write progress suc, {job_info.log_format()}.")
            return ret

        return inner

    return decorator


def write_progress_to_file(sub_job_detail: SubJobDetails, job_info: ExchangeJobInfo):
    log.info(f"Write progress  job_id: {job_info.job_id} ,sub_job_id : {job_info.sub_job_id}")
    sub_job_progress = get_progress_file_name(job_info)
    file_path = os.path.join(job_info.cache_path, sub_job_progress)
    log.info(f"Write progress: {sub_job_detail} to file: {file_path}")
    output_execution_result_ex(file_path, sub_job_detail.dict(by_alias=True))


def get_progress_file_name(job_info):
    if job_info.sub_job_id is None:
        sub_job_progress = f'exchange_{get_host_sn()}_{job_info.job_id}_'
    else:
        sub_job_progress = f'exchange_{get_host_sn()}_{job_info.job_id}_' + job_info.sub_job_id
    return sub_job_progress


def retry(exception_to_check=BaseException, tries=3, delay=5):
    """ 重试装饰器，用于在特定异常下进行函数级重试 """

    def deco_retry(func):
        @wraps(func)
        def f_retry(*args, **kwargs):
            cnt = 1
            while cnt <= tries:
                try:
                    return func(*args, **kwargs)
                except exception_to_check as exception:
                    log.warn(f'Caught exception: {str(exception)} then after {delay} seconds will do the {cnt}th retry')
                    time.sleep(delay)
                    cnt += 1
            return func(*args, **kwargs)

        return f_retry

    return deco_retry


def fill_log_info_param(log_details: list, job_info: ExchangeJobInfo):
    for item in log_details:
        if item["logInfo"] == ExchangeReportDBLabel.SUB_JOB_SUCCESS:
            item["logInfoParam"] = [job_info.sub_job_id]
        elif item["logInfo"] == ExchangeReportDBLabel.SUB_JOB_FALIED:
            item["logInfoParam"] = [job_info.sub_job_id]


def get_progress_path(copy_id, sub_job_id, cache_path):
    # 获取进度输出路径
    result_path = os.path.join(cache_path, 'tmp', copy_id, f'resultNotify_{sub_job_id}.txt')
    log.debug(f"Result path:{result_path}")
    return result_path

